﻿using System;
using System.Collections.Generic;
using UnityEngine;
using XLua;


// Lua脚本字段注入
[Serializable]
public class Injection
{
    public string name;
    public GameObject value;
    public string type;
}

/// <summary>
/// Lua逻辑通用可挂载脚本
/// </summary>
public class LuaBehaviour : MonoBehaviour
{
    [Header("LuaScript相对路径")]
    public string luaFilePath;
    [Header("字段注入")]
    public List<Injection> injections;

    private Action<LuaTable> onAwake;
    private Action<LuaTable> onStart;
    private Action<LuaTable> onEnable;

    private Action<LuaTable> onUpdate;
    private Action<LuaTable> onFixedUpdate;
    private Action<LuaTable> onLateUpdate;

    private Action<LuaTable> onDisable;
    private Action<LuaTable> onDestroy;

    private LuaTable module;
    public LuaTable Module
    {
        get 
        {
            if (module == null)
                ConnectLuaModule();
            return module; 
        }
    }

    void ConnectLuaModule()
    {
#if UNITY_EDITOR
        if (string.IsNullOrEmpty(luaFilePath))
        {
            Debug.LogError($"GameObject[{this.gameObject.name}] lua path invalid!");
            return;
        }
#endif
        LuaEnv luaEnv = LuaManager.instance.globalLuaEnv;

        luaEnv.DoString($"require '{luaFilePath}'");
        object[] list = luaEnv.DoString(@$"
local module = require '{luaFilePath}'

local function new()
    -- class_type, use module.new()
    if module.super ~= nil and module.vtbl ~= nil then
        return module.new()
    end
    -- normal object, set metatable
    local obj = {{}}
    setmetatable(obj, {{__index=module}})
    return obj
end

return new()
", luaFilePath);

        if (list != null && list.Length > 0)
        {
            module = list[0] as LuaTable;
        }
        else
        {
            Debug.LogError($"Load lua failed, path[{luaFilePath}]!");
        }
        module.Set("gameObject", gameObject);
        foreach (var injection in injections)
        {
            if (injection == null)
            {
                Debug.LogError($"Gameobject[{this.name}], Injection[{injection.name}] - [{injection.value}]");
                continue;
            }
            if (string.IsNullOrEmpty(injection.name) || injection.value == null)
            {
                Debug.LogError($"Gameobject[{this.name}], Injection[{injection.name}] - [{injection.value}]");
                continue;
            }

            if (injection.type == "GameObject" || injection.type == "")
            {
                module.Set(injection.name, injection.value);
            }
            else
            {
                module.Set(injection.name, injection.value.GetComponent(injection.type));
            }
        }

        module.Get("awake", out onAwake);
        module.Get("start", out onStart);
        module.Get("onenable", out onEnable);
        module.Get("update", out onUpdate);
        module.Get("fixedupdate", out onFixedUpdate);
        module.Get("lateupdate", out onLateUpdate);
        module.Get("ondisable", out onDisable);
        module.Get("ondestroy", out onDestroy);
    }

    #region Lua逻辑生命周期函数注册
    void Awake()
    {
        if (module == null)
        {
            ConnectLuaModule();
        }
        else
        {
            onAwake?.Invoke(module);
        }
    }
    void OnEnable()
    {
        onEnable?.Invoke(module);    
    }
    void Start()
    {
        onStart?.Invoke(module);    
    }
    void Update()
    {
        onUpdate?.Invoke(module);    
    }
    void FixedUpdate()
    {
        onFixedUpdate?.Invoke(module);
    }
    void LateUpdate()
    {
        onLateUpdate?.Invoke(module);
    }
    void OnDisable()
    {
        onDisable?.Invoke(module);
    }
    void OnDestroy()
    {
        onDestroy?.Invoke(module);

        onAwake = null;
        onStart = null;
        onEnable = null;
        onUpdate = null;
        onFixedUpdate = null;
        onLateUpdate = null;
        onDisable = null;
        onDestroy = null;

        module?.Dispose();
        injections = null;
    }
    #endregion
}